﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

/// <summary>
/// 
/// Surbowl.AspNetCore.Authentication.GitHubBearer.1.0.0
/// Versions: v1.0.0    2020-04-16
/// 
/// This authentication middleware use GitHub's OAuth service to authenticate users, 
/// so your light application can have no database of user information.
/// For example, in curl you can set the Authorization header like this:
///     <code> curl -H "Authorization: token OAUTH-TOKEN" http://localhost:5000/index </code>
/// 
/// Open source on GitHub: https://github.com/Surbowl/Surbowl.AspNetCore.Authentication.GitHubBearer
/// GitHub OAuth documentation: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
/// 
/// </summary>
namespace Surbowl.AspNetCore.Authentication.GitHubBearer
{
    public class GitHubBearerHandler : AuthenticationHandler<GitHubBearerOptions>
    {
        public GitHubBearerHandler(IOptionsMonitor<GitHubBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
                                  : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            // Try get token
            string token = Request.Headers[Options.TokenHeaderName];
            token = token?.Replace("token ", "").Trim();
            if (!string.IsNullOrEmpty(token))
            {
                try
                {
                    // Try access the GitHub user API for user information.
                    HttpClient client = new HttpClient();
                    client.DefaultRequestHeaders.Add("User-Agent", "PostmanRuntime/7.24.1");
                    client.DefaultRequestHeaders.Add("Authorization", $"token {token}");
                    var response = await client.GetAsync(GitHubBearerDefaults.UserInformationEndpoint);
                    if (response.IsSuccessStatusCode)
                    {
                        // Get user information
                        var strContent = await response.Content.ReadAsStringAsync();
                        JObject jsonContent = (JObject)JsonConvert.DeserializeObject(strContent);
                        var claimsIdentity = new ClaimsIdentity(GitHubBearerDefaults.AuthenticationScheme);
                        foreach (var j in jsonContent)
                        {
                            claimsIdentity.AddClaim(new Claim(j.Key.ToString(), j.Value.ToString()));
                        }
                        var principal = new ClaimsPrincipal(claimsIdentity);
                        TokenValidatedContext tokenValidatedContext = new TokenValidatedContext(base.Context, base.Scheme, base.Options)
                        {
                            Principal = principal
                        };
                        tokenValidatedContext.Success();
                        return tokenValidatedContext.Result;
                    }
                }
                catch (Exception e)
                {
                    return AuthenticateResult.Fail(e);
                }
            }

            return AuthenticateResult.NoResult();
        }
    }
}